// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include <sstream>
#include <optional>

#include "PackageCompileInfo.h"

PackageCompileInfo::PackageCompileInfo(std::string packagePath, std::vector<std::string> importPath)
    : packagePath(std::move(packagePath)), importPath(std::move(importPath))
{}

std::string PackageCompileInfo::ToString() const
{
    std::ostringstream oss;
    oss << "PackageCompileInfo {\n";
    oss << "  packagePath: \"" << packagePath << "\",\n";
    oss << "  importPath: [";
    for (size_t i = 0; i < importPath.size(); ++i) {
        oss << "\"" << importPath[i] << "\"";
        if (i + 1 < importPath.size()) {
            oss << ", ";
        }
    }
    oss << "]\n}";
    return oss.str();
}

static std::vector<std::string> Tokenize(const std::string &line)
{
    std::vector<std::string> tokens;
    std::string cur;
    bool inQuote = false;

    for (auto ch : line) {
        if (ch == '"') { // Toggle quote state, don't include quotes
            inQuote = !inQuote;
        } else if (ch == ' ' && !inQuote) {
            if (!cur.empty()) {
                tokens.push_back(cur);
                cur.clear();
            }
        } else {
            cur.push_back(ch);
        }
    }
    if (!cur.empty()) {
        tokens.emplace_back(std::move(cur));
    }
    return tokens;
}

static std::pair<std::string, std::size_t> ParsePackagePath(const std::vector<std::string> &tokens, std::size_t start)
{
    std::string path;
    std::size_t i = start;
    while (i < tokens.size() && tokens[i].rfind("-", 0) != 0) {
        if (!path.empty()) {
            path += ' ';
        }
        path += tokens[i];
        ++i;
    }
    return {std::move(path), i};
}

static std::pair<std::optional<std::string>, std::size_t> ParseImportPath(
    const std::vector<std::string> &tokens, std::size_t start)
{
    const std::string &token = tokens[start];
    std::string path;
    std::size_t i = start;

    auto eqPos = token.find('=');
    if (eqPos != std::string::npos && eqPos + 1 < token.size()) {
        path = token.substr(eqPos + 1);
    } else {
        ++i;
        while (i < tokens.size() && tokens[i].rfind("-", 0) != 0) {
            if (!path.empty()) path += ' ';
            path += tokens[i++];
        }
        --i;
    }

    if (!path.empty()) {
        return {std::move(path), i + 1};
    } else {
        return {std::nullopt, i + 1};
    }
}

std::optional<PackageCompileInfo> PackageCompileInfo::TryCreate(const std::string &command)
{
    const auto tokens = Tokenize(command);

    auto packagePath = std::string("");
    auto importPath = std::vector<std::string>();

    std::size_t i = 0;
    while (i < tokens.size()) {
        const std::string &token = tokens[i];

        if (token == "-p") {
            ++i;
            if (i >= tokens.size()) break;
            auto [path, next] = ParsePackagePath(tokens, i);
            packagePath = std::move(path);
            i = next;
            continue;
        }

        if (token.rfind("--import-path", 0) == 0) {
            auto [pathOpt, next] = ParseImportPath(tokens, i);
            if (pathOpt.has_value()) {
                importPath.push_back(std::move(*pathOpt));
            }
            i = next;
            continue;
        }

        ++i;
    }

    if (packagePath.empty()) {
        return std::nullopt;
    }

    return PackageCompileInfo(std::move(packagePath), std::move(importPath));
}

std::vector<PackageCompileInfo> PackageCompileInfo::TryCreate(const std::vector<std::string> &commands)
{
    std::vector<PackageCompileInfo> result;
    for (const auto& command : commands) {
        if (auto info = TryCreate(command); info.has_value()) {
            result.emplace_back(info.value());
        }
    }
    return result;
}
